using System.Collections.Generic;
using System.Linq;
using NopSolutions.NopCommerce.BusinessLogic.Categories;
using NopSolutions.NopCommerce.BusinessLogic.Products.Specs;
using NopSolutions.NopCommerce.LuceneHelper.Queries;

namespace NopSolutions.NopCommerce.LuceneHelper.Tasks
{
    public class SpecificationUpdateTask : BaseTask<SpecificationUpdateTask>
    {
        #region Private

        private SpecificationAttribute _specificationAttribute;
        private bool _updateProducts = true;
        private bool _updateOptions = true;

        #endregion

        #region Constructors

        public SpecificationUpdateTask(int specificationAttributeId)
            : base(specificationAttributeId)
        {
        }

        public SpecificationUpdateTask(int specificationAttributeId, bool updateProducts)
            : this(specificationAttributeId)
        {
            _updateProducts = updateProducts;
        }

        public SpecificationUpdateTask(int specificationAttributeId, bool updateProducts, bool updateOptions)
            : this(specificationAttributeId, updateProducts)
        {
            _updateOptions = updateOptions;
        }

        public SpecificationUpdateTask(SpecificationAttribute specificationAttribute)
            :this(specificationAttribute.SpecificationAttributeId)
        {
            _specificationAttribute = specificationAttribute;
        }

        public SpecificationUpdateTask(SpecificationAttribute specificationAttribute, bool updateProducts)
            :this(specificationAttribute.SpecificationAttributeId, updateProducts)
        {
            _specificationAttribute = specificationAttribute;
        }

        public SpecificationUpdateTask(SpecificationAttribute specificationAttribute, bool updateProducts, bool updateOptions)
            : this(specificationAttribute.SpecificationAttributeId, updateProducts, updateOptions)
        {
            _specificationAttribute = specificationAttribute;
        }

        #endregion

        #region Properties

        public int SpecificationAttributeId
        {
            get { return Id; }
        }

        public bool UpdateProducts
        {
            get { return _updateProducts; }
            set { _updateProducts = value; }
        }

        public bool UpdateOptions
        {
            get { return _updateOptions; }
            set { _updateOptions = value; }
        }

        public SpecificationAttribute SpecificationAttribute
        {
            get
            {
                return _specificationAttribute ??
                       (_specificationAttribute =
                        SpecificationAttributeManager.GetSpecificationAttributeById(SpecificationAttributeId));
            }
        }

        #endregion

        #region Task

        public override void DoTask(IndexManager indexManager, SearchManager searchManager, IndexWriterWrapper indexer)
        {
            #region Delete existing attribute
            
            var specQuery = new SpecificationAttributeQuery().Where(SpecificationAttributeQuery.FIELDS_SPECIFICATION_ATTRIBUTE_ID,
                                                           SpecificationAttributeId.ToString());
            indexer.DeleteDocuments(specQuery.Query);

            #endregion

            if (SpecificationAttribute != null)
            {
                #region Index attribute

                var document = SpecificationAttribute.Document();

                #region Index categories

                foreach (var categoryId in DatabaseHelper.GetCategoriesForAttribute(SpecificationAttributeId))
                {
                    var breadCrumbs = CategoryManager.GetBreadCrumb(categoryId);
                    document.AddField(SpecificationOptionQuery.FIELDS_CATEGORY_ID, categoryId.ToString());
                    foreach (var crumb in breadCrumbs)
                    {
                        document.AddField(SpecificationAttributeQuery.FIELDS_CATEGORY_ID, crumb.CategoryId.ToString());
                    }
                }

                #endregion

                #region Index manufacturers

                foreach (var manufacturerId in DatabaseHelper.GetManufacturersForAttribute(SpecificationAttributeId))
                {
                    document.AddField(SpecificationAttributeQuery.FIELDS_MANUFACTURER_ID, manufacturerId.ToString());
                }

                #endregion

                indexer.AddDocument(document);

                #endregion

                if (UpdateOptions)
                {
                    #region Index all options

                    indexManager.IndexTasks(
                        SpecificationAttributeManager.GetSpecificationAttributeOptionsBySpecificationAttribute(
                            SpecificationAttributeId).Select(
                                specificationAttributeOption =>
                                new SpecificationOptionUpdateTask(
                                    specificationAttributeOption.SpecificationAttributeOptionId, UpdateProducts)));

                    #endregion
                }
            }
            else
            {
                #region Delete all options for this attribute since it doesn't exist anymore

                indexManager.IndexTask(new DelegateTask((x, y, z) => z.DeleteDocuments(
                    new SpecificationOptionQuery().Where(
                        SpecificationOptionQuery.
                            FIELDS_SPECIFICATION_ATTRIBUTE_ID,
                        SpecificationAttributeId.ToString()).Query), LuceneIndexType.SpecificaitonOptions));

                #endregion
            }
        }

        public override LuceneIndexType IndexType
        {
            get { return LuceneIndexType.SpecificationAttributes; }
        }

        public override bool RebuildIndex
        {
            get { return false; }
        }

        public override IEnumerable<System.Func<SpecificationUpdateTask, object>> ComparableProperties()
        {
            return new List<System.Func<SpecificationUpdateTask, object>> {x => x.UpdateProducts, x => x.UpdateOptions};
        }

        public override void UpdateTask(SpecificationUpdateTask newTask)
        {
            if (!UpdateProducts)
            {
                UpdateProducts = newTask.UpdateProducts;
            }
            if (!UpdateOptions)
            {
                UpdateOptions = newTask.UpdateOptions;
            }
        }

        #endregion
    }
}
